/**
 * @fileOverview
 * WashizuMahjong.
 * http://code.google.com/p/washizu-mahjong/
 */
/**
 * @namespace
 */
var Game = new function(){
	/**
	 * To set the turn of the players.
	 * @type Number
	 * @see Player#chowAction
	 * @see Player#kongAction
	 * @see Player#pickUp
	 * @see Player#pongAction
	 * @see Player#toDiscardTile
	 */
	this.turn = 0;
	/**
	 * @type Boolean
	 * @see Game.countScore
	 * @see Game.drawRound
	 * @see Game.finishRound
	 */
	var isRoundFinish = false;
	/**
	 * Whether the dealer win in the round.
	 * @type Boolean
	 */
	var isDealerWin = false;
	/**
	 * Whether the round is finished by abortive draw.
	 * @type Boolean
	 * @see Game.AbortiveDraws
	 */
	var isAbortiveDraw = false;
	/**
	 * @type Boolean
	 * @see Game.AbortiveDraws.nineYaokyuTiles
	 */
	var isDealerMakeAbortiveDraw = false;
	/**
	 * When the game support mutiple-ron and one player is ronned by mutiple players, this array will save the details of all players winned.
	 * @type Player[]
	 * @see Game.countScore
	 */
	var mutiRonPlayer = [];
	/**
	 * @type Number[]
	 * @see Game-mutiRonPlayer
	 * @see Game.countScore
	 */
	var scorePositionOfStart = [];
	/**
	 * The object of Round Detail.
	 * If it is tripple ron, the new round should be change as a draw round.
	 * This object is used for saving the detail of the round.
	 * @see Rules.Triple_Ron
	 * @see RoundDetail.number
	 * @see RoundDetail.code
	 * @see RoundDetail.counterSticks
	 * @see RoundDetail.dealer
	 * @see Game.AbortiveDraws.tripleRon
	 */
	var _roundDetail = {
		number: null,
		code: null,
		counterSticks: null,
		dealer: null
	};
	this.__defineGetter__("isRoundFinish", function(){
		if(Rules.Multiple_Agari && mutiRonPlayer.length > 0){
			return true
		}
		return isRoundFinish
	});
	/**
	 * The max numbers of discarded tiles
	 * @constant
	 * @type Number
	 * @see RoundDetail.pickedUpTilesNum
	 */
	this.__defineGetter__("MaxPickedTilesNum", function(){
		return DEFAULT_TILES.length - 13 * players.length - 14
	});	
	/**
	 * @param {Mahjong[]} $$ The same as DEFAULT_TILES.
	 * @see Game.startNewGame
	 * @see DEFAULT_TILES
	 */
	function setTiles($$){
		//$$ : default tiles; $$$ : the tiles used in game
		if(!(typeof($$)=="object" && ($$ instanceof Array)))return;
		var t="\u6771\u5357\u897f\u5317\u4e2d\u767c\u767d\u842c\u7d22\u9905".split(""),
		    chinesetext="\u4e00\u4e8c\u4e09\u56db\u4e94\u516d\u4e03\u516b\u4e5d".split(""),
		    typeList=["\u5b57\u724c","\u842c","\u7d22","\u9905"];
		try{
			//make hiddenTile
			hiddenTile = new MahjongTile(" ", " ", "&#127019;", 1, Image_Foleders_of_Mahjong_Tiles.Back_Tile_Image);
		}catch(e){}

		for(var i = 126976; i <= 127009; i++){//0xD83C 0xDC00 -> 0xD83C 0xDC21 + 0xD83C 0xDC2B
			var mahjongListIndex = i - 126976;
			var n,type;
			var tileImgUrl;
			if(mahjongListIndex < 10)	{tileImgUrl="Tile00"+mahjongListIndex+".png";}
			else				{tileImgUrl="Tile0"+parseInt(mahjongListIndex)+".png";}

			if(mahjongListIndex < 7){
				//字牌
				type =  typeList[0];
				n = t[mahjongListIndex];
				for(var f = 0; f < 4 ; f++){
					var _m = new MahjongTile(n, type, "&#" + i + ";", (f == 0), tileImgUrl);
					_m.id = $$.length;
					$$.push(_m);
				}
			}else{
				type = typeList[parseInt((mahjongListIndex - 7) / 9)+1];
				n = chinesetext[parseInt((mahjongListIndex - 7) % 9)] + type;
				for(var f = 0; f < 4 ; f++){
					var canRedDora = (f%2 == 0 && parseInt((mahjongListIndex - 7) % 9) + 1 == Rules.Red_Dora_Num && Rules.Red_Dora);
					var _m = new MahjongTile(n, type, "&#" + i + ";", (f == 0), tileImgUrl, canRedDora);
					_m.id = $$.length;
					$$.push(_m);
				}
			}

		}
	}

	/**
	 * To start a new round.
	 */
	function startNewRound(){
		ActionMessage(ActionMessage.NewRound, null, null, null, JSON.parse(JSON.stringify(RoundDetail)));
		//reset tiles, dora
		gameTiles = DEFAULT_TILES.clone();
		Dora.clean()
		RoundDetail.kongTimes = 0;
		RoundDetail.pickedUpTilesNum = 0;
		RoundDetail.discardTiles = []
		//reset 13-tile hand
		for(var i = 0; i < players.length; i++){	//4 players
			players[i].clean();
			for(var j = 0; j < 13; j++){	//13-tile hand
				var tile = gameTiles[parseInt(Math.random() * gameTiles.length)];
				players[i].hand.push(tile);
				gameTiles.remove(tile);
				tile.owner = {
					id: i,
					name: players[i].name
				}
				ActionMessage(ActionMessage.AddTile, players[i], tile);
			}
			Hand.sort(players[i].hand);
		}
		//set dora
		Dora.addDora();
		
		
		//Let the dealer start the round
		Game.turn = RoundDetail.dealer;
		players[RoundDetail.dealer].pickUp();
		players[RoundDetail.dealer].run();

		isRoundFinish = false;
		isDealerWin = false;
		isAbortiveDraw = false;
		isDealerMakeAbortiveDraw = false;
		mutiRonPlayer = [];
		scorePositionOfStart = [];

	};
	/**
	 * Make chage of the score of each player.
	 */
	function _countScore(scoreresult, winner, playerDiscardedAgariTile){
		if((playerDiscardedAgariTile && !(playerDiscardedAgariTile instanceof Player)) ||
		   playerDiscardedAgariTile == winner){
			playerDiscardedAgariTile = null
		}
		var isDealer = (winner.id == RoundDetail.dealer);
		isDealerWin = isDealer;

		if(!playerDiscardedAgariTile){
			//Self Drawn
			players.forEach(function(val, index, arr){
				if(val == winner) return;
				var paidScore = scoreresult.score;
				if(isDealer){
					paidScore = paidScore * players.length * 1.5 / (players.length - 1);
				}
				if(val.id == RoundDetail.dealer){
					paidScore *= 2;
				}

				paidScore = Math.ceil(paidScore / 100) * 100;
				paidScore += RoundDetail.counterSticks * Rules.Score_Per_Sticks;
				val.score -= paidScore;
				return new ActionMessage(ActionMessage.PayScore, val, null, null, {score: paidScore, ScoreResult: scoreresult, isDealer: (val.id == RoundDetail.dealer)});
			});
			var getScore = scoreresult.score * (players.length - 1);
			if(isDealer){
				getScore *= 2
			}else{
				getScore += scoreresult.score;
			}
			getScore += RoundDetail.counterSticks * Rules.Score_Per_Sticks * (players.length - 1);
			winner.score += getScore
			ActionMessage(ActionMessage.ReciveScore, winner, null, null, {score: getScore, ScoreResult: scoreresult, isDealer: isDealer});
		}else{
			var paidScore = scoreresult.score * 4;
			if(isDealer){
				paidScore *= 1.5;
			}
			paidScore = Math.ceil(paidScore / 100) * 100;
			paidScore += RoundDetail.counterSticks * Rules.Score_Per_Sticks * (players.length - 1);
			winner.score += paidScore;
			playerDiscardedAgariTile.score -= paidScore;

			ActionMessage(ActionMessage.PayScore, playerDiscardedAgariTile, null, null, {score: paidScore, ScoreResult: scoreresult, isDealer: (playerDiscardedAgariTile.id == RoundDetail.dealer)});
			ActionMessage(ActionMessage.ReciveScore, winner, null, null, {score: paidScore, ScoreResult: scoreresult, isDealer: isDealer});
		}
		//Recycle the richi counter
		players.forEach(function(val, index, arr){
			if(val.isRichi || val.isDoubleRichi){
				winner.score += 1000;
				ActionMessage(ActionMessage.ReciveScore, winner, null, null, {score: 1000});
			}
		});
	}
	/**
	 * To star new game.
	 */
	this.startNewGame = function(){
		RoundDetail.dealer = Math.floor(Math.random() * players.length)
		setTiles(DEFAULT_TILES);
		startNewRound();
	};
	/**
	 * After finish the round, setting up the change of the score.
	 * @see RoundDetail
	 * @param {ScoreResult}	scoreresult
	 * @param {Player}	winner
	 * @param {Player}	[playerDiscardedAgariTile]
	 */
	this.countScore = function(scoreresult, winner, playerDiscardedAgariTile){
		if(isRoundFinish && !Rules.Multiple_Agari){
			// When only support one player be Roned
			return false;
		}else if(Rules.Multiple_Agari && mutiRonPlayer.indexOf(winner) >= 0){
			return false;
		}
		if(!scoreresult || !winner || !(scoreresult instanceof ScoreResult) || !(winner instanceof Player) || (playerDiscardedAgariTile && !(playerDiscardedAgariTile instanceof Player))){
			// The variable has some issue
			return false;
		}
		if(Rules.Multiple_Agari){
			mutiRonPlayer.push(winner);
			if(scorePositionOfStart.length == 0){
				// The first ron player
				players.forEach(function(val){
					scorePositionOfStart.push(val.score);
				});
				_roundDetail = {
					number: RoundDetail.number,
					code: RoundDetail.code,
					counterSticks: RoundDetail.counterSticks,
					dealer: RoundDetail.dealer
				}
			}
		}


		if(mutiRonPlayer.length == 3 && Rules.Triple_Ron){
			//If this part run, it meas other 2 winning players have aleady count score.
			for(var i = 0; i < players.length; i++){
				players[i].score = scorePositionOfStart[i];
			}
			//Therefore
			Game.AbortiveDraws.tripleRon()
		}else{
			_countScore(scoreresult, winner, playerDiscardedAgariTile);
		}
		isRoundFinish = true;
		return true;
	};
	/**
	 * Normal draw.
	 */
	this.drawRound = function(){
		if(RoundDetail.pickedUpTilesNum < bgColor="transparent" || isRoundFinish){
			return false
		}
		var readyPlayers = [],		//the player who has ready hand
		    nagashiPlayers = [];	//the player who can 流し満貫
		players.forEach(function(_player, index, arr){
			if(_player.isReadyHand.length > 0){
				readyPlayers.push(_player);
			}
			var canNagashi = true;
			_player.discardTiles.forEach(function(_tile, _index, _arr){
				if(!_tile.isYaoChuuTile || _tile.taker){
					canNagashi = false;
				}
			});
			if(canNagashi){
				nagashiPlayers.push(_player);
			}
		});
		if(nagashiPlayers.length > 0){
			var sr = new ScoreResult;
			sr.score = 2000; sr.yakus = ["\u6d41\u3057\u6e80\u8cab"];
			nagashiPlayers.forEach(function(val, index, arr){
				_countScore(sr, val);
			});
			isRoundFinish = true;
			isDealerWin = nagashiPlayers.indexOf(players[RoundDetail.dealer]) >= 0;
			return sr;
		}else if(readyPlayers.length > 0 ){
			if(readyPlayers.length != players.length){
				var totalPaidScore = (players.length - 1) * 1000,
				    paidScore = totalPaidScore / (players.length - readyPlayers.length), //the score paid to each player with ready hand.
				    getScore = totalPaidScore / readyPlayers.length;	//the score each player with ready hand get.
				players.forEach(function(val, index, arr){
					if(readyPlayers.indexOf(val)>=0){
						val.score += getScore;
						ActionMessage(ActionMessage.ReciveScore, val, null, null, {score: getScore ,isDealer: (val.id == RoundDetail.dealer)});
					}else{
						val.score -= paidScore;
						ActionMessage(ActionMessage.PayScore, val, null, null, {score: paidScore, isDealer: (val.id == RoundDetail.dealer)});
					}
				});
			}
		}
		//if dealer has ready hand
		if(readyPlayers.indexOf(players[RoundDetail.dealer]) >= 0){
			isDealerWin = true;
		}
		isRoundFinish = true;
	};
	/**
	 * Finish the round and start a new round.
	 * @see Game-isDealerWin
	 * @see Game-startNewRound
	 */
	this.finishRound = function(){
		if(!isRoundFinish){
			return false;
		}
		if(isDealerWin || isDealerMakeAbortiveDraw){
			RoundDetail.counterSticks++;
		}else{
			RoundDetail.number++;
			if(RoundDetail.number > players.length){
				RoundDetail.number = 1;
				RoundDetail.code++;
			}
			RoundDetail.dealer = (RoundDetail.dealer + 1) % players.length;
			if(isAbortiveDraw){
				// abortive draw
				RoundDetail.counterSticks++;
			}else{
				// not draw
				RoundDetail.counterSticks = 0;
			}
		}
		console.log(RoundDetail.round_name);
		startNewRound();
		return true;
	};

	/**
	 * Abortive draws.
	 * @namespace
	 */
	this.AbortiveDraws = new function(){
		/**
		 * The default callback function of the abortive draw.
		 */
		function defaultCallback(draw_type){
			alert(draw_type)
		}
		this.__defineSetter__("callback", function(callback_function){
			if(typeof callback_function != "function") return;
			this.__defineGetter__(callback, callback_function);
		});
		this.__defineGetter__("callback", defaultCallback);

		/**
		 * 四開槓／四槓流局
		 * @return {ActionMessage|Boolean}
		 */
		this.fourKongs = function(){
			if(RoundDetail.kongTimes != 4){
				return false;
			}
			var can4Kongs = true;
			players.forEach(function(p){
				if(!can4Kongs)
					return;
				var kongTimes = 0;
				p.meld.forEach(function(m){
					if(m.type == MeldType.KONG){
						kongTimes++;
					}
				});
				if(kongTimes == RoundDetail.kongTimes){
					can4Kongs = false;
				}
			});
			if(!can4Kongs)
				return false;
			isRoundFinish = true;
			isAbortiveDraw = true;
			Game.AbortiveDraws.callback("四槓流局");
			return new ActionMessage(ActionMessage.AbortiveDraw, null, null, null, {type: "四槓流局"})
		}
		/**
		 * 四家立直
		 * @return {ActionMessage|Boolean}
		 */
		this.fourRichis = function(){
			if(!Rules.Four_Richies){
				return false;
			}
			//check 4 richies
			var isAllRichi = true;
			players.forEach(function(val, index, arr){
				if(!isAllRichi){
					return;
				}
				if(!val.isRichi && ! val.isDoubleRichi){
					isAllRichi = false;
				}
			});
			if(!isAllRichi){
				return false;
			}
			//finish checking
			isRoundFinish = true;
			isAbortiveDraw = true;
			Game.AbortiveDraws.callback("四家立直");
			return new ActionMessage(ActionMessage.AbortiveDraw, null, null, null, {type: "四家立直"});
		}
		/**
		 * 四風連打
		 * @return {ActionMessage|Boolean}
		 */
		this.fourWinds = function(){
			if(Game.turn != 4){
				return false
			}
			var is4Wind = true, _4wind = null;
			players.forEach(function(p,index, arr){
				if(p.discardTiles.length != 1 ||
				   p.meld.length != 0 ||
				   p.discardTiles[0].type != MahjongType.Word ||
				   p.discardTiles[0].wordNum == MahjongWordNumCode.Haku ||
				   p.discardTiles[0].wordNum == MahjongWordNumCode.Hatsu ||
				   p.discardTiles[0].wordNum == MahjongWordNumCode.Chun ||
				   !is4Wind
				   ){
					is4Wind = !1;
					return;
				}
				if(!_4wind){
					_4wind = p.discardTiles[0];
				}else if(!_4wind.isSameTile(p.discardTiles[0])){
					is4Wind = !1;
					return;
				}
			});
			if(!is4Wind){
				return false;
			}
			// if it can be 四風連打
			isRoundFinish = true;
			isAbortiveDraw = true;
			Game.AbortiveDraws.callback("四風連打");
			return new ActionMessage(ActionMessage.AbortiveDraw, null, null, null, {type: "四風連打"})
		}
		/**
		 * 三家和／トリプルロン
		 * @see Rules.Multiple_Agari
		 * @see Rules.Triple_Ron
		 * @return {ActionMessage|Boolean}
		 */
		this.tripleRon = function(){
			if(!Rules.Multiple_Agari || !Rules.Triple_Ron || mutiRonPlayer.length != 3){
				return false;
			}
			//finish checking
			isRoundFinish = true;
			isAbortiveDraw = true;
			Game.AbortiveDraws.callback("三家和");
			return new ActionMessage(ActionMessage.AbortiveDraw, null, null, null, {type: "三家和"});
		}
		/**
		 * To check whether current the player's hand can make 九種九牌
		 * @return {Boolean}
		 */
		this.canNineYaokyuTiles = function(){
			if(Game.turn > 4){
				return false;
			}
			var isAllPlayerDoorClear = true;
			for(var i = RoundDetail.dealer, j = 0; j < players.length ; i = (i + 1) % players.length, j++){
				if(players[i].meld.length > 0 || (i != Game.turn && players[i].discardTiles.length > 1)){
					isAllPlayerDoorClear = false;
				}
				if(i == Game.turn || !isAllPlayerDoorClear){
					break;
				}
			}
			if(!isAllPlayerDoorClear){
				return false;
			}

			var h = players[Game.turn].hand.clone(), result = [];
			Hand.sort(h);
			h.forEach(function(_tile, arr, index){
				if(_tile.typeCode == MahjongType.Word){
					if(result.indexOf(_tile.tileName) == -1){
						result.push(_tile.tileName);
					}
				}else if(_tile.simpleNum == 1 || _tile.simpleNum == 9){
					if(result.indexOf(_tile.tileName) == -1){
						result.push(_tile.tileName);
					}
				}
			});
			if(result.length >= 9){
				return true;
			}
			return false;
		}
		/**
		 * 九種九牌／キューシュキューハイ
		 * @see Game.AbortiveDraws.canNineYaokyuTiles
		 * @return {ActionMessage|Boolean}
		 */
		this.nineYaokyuTiles = function(){
			//check 九種九牌
			if(!Game.AbortiveDraws.canNineYaokyuTiles()){
				return false;
			}
			//finish checking
			isRoundFinish = true;
			isAbortiveDraw = true;
			if(Game.turn == RoundDetail.dealer){
				isDealerMakeAbortiveDraw = true;
			}
			Game.AbortiveDraws.callback("九種九牌");
			return new ActionMessage(ActionMessage.AbortiveDraw, null, null, null, {type: "九種九牌"});
		}
	};
}